package com.cskaoyan.javase.oop3._2inner._5lambda._0introduction;

/**
 * Lambda表达式的引入
 * Lambda 表达式是 JDK8 的一个新特性，可以取代接口的匿名内部类，写出更优雅的Java代码。
 * 如果说匿名内部类实际上是局部内部类的更进一步，简化了局部内部类
 * 那么Lambda就是匿名内部类更进一步，语法上更简洁了，代码更优雅了，是高端的玩法，是人上人的玩法。
 *
 * 以上描述总体就说了三点：
 *      1.Lambda表达式仍然是局部内部类，是特殊的局部内部类，仍然定义在局部位置。
 *              而且局部内部类的注意事项，也一样存在。
 *      2.Lambda表达式在取代匿名内部类时，不是全部都取代，
 *              而是取代接口的匿名内部类，而类的匿名内部类Lambda表达式是不能取代的。
 *
 *      3.Lambda表达式是匿名内部类的更进一步，
 *          所以Lambda表达式得到的也不是一个类，而是一个对象，并且是接口的子类对象。
 *
 * 在具体讲解和使用Lambda表达式的语法之前,先讲解一个概念:
 * Lambda表达式取代接口的匿名内部类,并不是所有的接口都可以,而是要求这个接口:
 *      有且必须只有一个强制子类实现的抽象方法
 *      在Java中这种接口称之为功能接口(Functional Interface)
 *      Lambda表达式是取代功能接口的匿名内部类.
 *      为了更好的使用功能接口,Java提供了注解"@FunctionalInterface"
 *      用该注解标记接口,如果接口是功能接口就不会报错,反之就会编译报错
 *      对于功能接口而言:
 *      思考:
 *      1.功能接口中只能存在一个方法吗?
 *          不是,Java8之后接口中允许定义默认方法和静态方法,这两种方法不需要子类实现.
 *      2.功能接口中只能存在一个抽象方法吗?
 *          不是,实际上来说,某些抽象方法不需要子类实现
 *          考虑Java中每一个类的父类Object
 *          那么如果该抽象方法,可以用Object类的方法作为实现
 *          那么该抽象方法,就不需要子类实现.
 * 当然开发中,多数情况下,功能接口中还是只有一个抽象方法的,就是强制子类实现的抽象方法.
 *
 * 功能接口有了以后,就可以根据Lambda表达式的语法来写功能接口的子类对象
 * Lambda表达式的语法:
 * () -> {}
 * 解释:
 *      1."()"是功能接口中,那个必须要子类实现的抽象方法的形参列表
 *          直接照抄抽象方法的形参列表即可
 *
 *      2,"->"由英文横杠和大于号组成,它是Lambda表达式运算符,读作"goes to"
 *          它是固定的语法结构
 *
 *      3."{}"
 *          Lambda表达式当中的{}不是类体,而是功能接口中那个必要子类实现的抽象方法的
 *              重写方法体
 *              它是一个方法体,而不是类体
 *
 *      Lambda表达式为什么要求接口是功能接口?
 *      原因在于Lambda表达式的语法中,不能重写两个抽象方法,只能重写一个抽象方法.
 *
 * 直接在局部位置写上述语法,肯定是会报错的
 * 为什么?
 * 因为直接写语法,编译器无从得到该Lambda表达式创建的究竟是谁的子类对象
 * 所以需要程序员手动告诉编译器Lambda表达式创建的子类对象究竟是哪个功能接口的子类对象
 * 这个过程,称之为"Lambda表达式的类型推断"
 * Lambda表达式的类型推断是非常重要的,使用Lambda表达式必须完成类型推断,否者不能通过编译
 *
 * Lambda表达式的类型推断,推断需要额外信息,需要根据代码的上下文推断,有以下方式:
 *      1.直接用功能接口的引用接收它,用父类引用接收
 *          并且由于Lambda表达式的{}是重写的方法体
 *          所以Lambda表达式所创建的子类对象不能新增自身独有成员
 *          所以用父类引用接收没有不能访问子类独有成员的缺点
 *
 *      2.(了解)使用Lambda表达式的特殊语法
 *      类似于强制类型转换
 *      ((功能接口名)Lambda表达式).使用
 *      注意这种方式必须直接使用调用方法.
 *
 *      以下两种方式更常见,借助方法来完成类型推断
 *      3.借助方法的返回值类型,完成类型推断
 *      4.借助方法的形参数据类型,完成类型推断
 *
 * 以上就是Lambda表达式的基本使用.
 *
 * @since 11:24
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // Lambda表达式仍然定义在局部位置,得到的是功能接口的子类对象
        // a引用指向接口IA的子类对象
        IA a = () -> {
            System.out.println("我是Lambda表达式,我正在摇晃着🍷,高端!");
        };
        a.test();

        ((IA) () -> {
            System.out.println("我来自上流社会!");
        }).test();

        method().test();

        method(()->{
            System.out.println("我今天中午要吃鸡公煲!");
        });
    }

    public static void method(IA a){
        a.test();
    }

    public static IA method() {
        return () -> {
            System.out.println("我是仌!");
        };
    }
}

@FunctionalInterface
interface IA {
    void test();
}

@FunctionalInterface
interface IC {
    void test();
}


@FunctionalInterface
interface IB {
    void test();

    default void test2() {
    }

    static void test3() {
    }

    // 定义一个可以用Object类当中的equals方法作为默认实现的抽象方法
    boolean equals(Object o);

    // 定义一个可以用Object类当中的hashCode方法作为默认实现的抽象方法
    int hashCode();
}

/*
@FunctionalInterface
interface IB {
}
*/
